﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Management;
using System.Runtime.InteropServices;

using HANDLE = System.IntPtr;

namespace TaskManager
{
	[Serializable]
	public class ProcessInfo
	{
		private const int TOKEN_QUERY = 0x00000008; // Магия чисел :)

		enum TOKEN_INFORMATION_CLASS
		{
			TokenUser = 1,
			TokenGroups,
			TokenPrivileges,
			TokenOwner,
			TokenPrimaryGroup,
			TokenDefaultDacl,
			TokenSource,
			TokenType,
			TokenImpersonationLevel,
			TokenStatistics,
			TokenRestrictedSids,
			TokenSessionId
		}

		[StructLayout(LayoutKind.Sequential)]
		struct TOKEN_USER
		{
			public _SID_AND_ATTRIBUTES User;
		}

		[StructLayout(LayoutKind.Sequential)]
		struct _SID_AND_ATTRIBUTES
		{
			public IntPtr Sid;
			public int Attributes;
		}

		#region Importing WinAPI DLLs

		[DllImport("advapi32")]
		static extern bool OpenProcessToken(HANDLE ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

		[DllImport("kernel32.dll")]
		static extern HANDLE GetCurrentProcess();

		[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
		static extern bool GetTokenInformation(	HANDLE hToken, TOKEN_INFORMATION_CLASS tokenInfoClass,
												IntPtr TokenInformation, int TokeInfoLength, ref int reqLength);

		[DllImport("kernel32.dll")]
		static extern bool CloseHandle(HANDLE handle);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
		static extern bool ConvertSidToStringSid(IntPtr pSID, [In, MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
		static extern bool ConvertStringSidToSid([In, MarshalAs(UnmanagedType.LPTStr)] string pStringSid, ref IntPtr pSID);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
		static extern bool LookupAccountSid
		(
			[In, MarshalAs(UnmanagedType.LPTStr)] string lpSystemName,
			IntPtr pSid,
			StringBuilder Account,
			ref int cbName,
			StringBuilder DomainName,
			ref int cbDomainName,
			ref int peUse
		);

		#endregion

		private int cpuUsage;
		public int CPU_Usage
		{
			get { return cpuUsage; }			
		}

		private string name;
		public string Name
		{
			get { return name; }			
		}

		private int pid;
		public int PID
		{
			get { return pid; }			
		}

		private ProcessPriorityClass priority;
		public ProcessPriorityClass Priority
		{
			get { return priority; }			
		}

		private long memory;
		public long Memory
		{
			get { return memory; }			
		}

		private string user;
		public string User
		{
			get { return user; }
		}

		private bool allowed = true;
		public bool Allowed
		{
			get { return allowed; }			
		}

		public override bool Equals(object o)
		{
			ProcessInfo proc = o as ProcessInfo;
			if (proc == null)
				return false;

			return (proc.PID == this.PID &&
				proc.CPU_Usage == this.CPU_Usage &&
				proc.Memory == this.Memory &&
				proc.Name == this.Name &&
				proc.Priority == this.Priority &&
				proc.User == this.User);				
		}

		private static bool DumpUserInfo(HANDLE pToken, out IntPtr SID)
		{
			int Access = TOKEN_QUERY;
			HANDLE procToken = HANDLE.Zero;
			bool ret = false;

			SID = IntPtr.Zero;

			try
			{
				if (OpenProcessToken(pToken, Access, ref procToken))
				{
					ret = ProcessTokenToSid(procToken, out SID);
					CloseHandle(procToken);
				}
				return ret;
			}
			catch
			{
				return false;
			}
		}

		private static bool ProcessTokenToSid(HANDLE token, out IntPtr SID)
		{
			TOKEN_USER tokUser;
			const int tokenInformationLength = 256;

			IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInformationLength);
			
			bool ret = false;
			SID = IntPtr.Zero;

			try
			{
				int t = tokenInformationLength;
				ret = GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenUser, tokenInformation, t, ref t);

				if (ret)
				{
					tokUser = (TOKEN_USER)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_USER));
					SID = tokUser.User.Sid;
				}

				return ret;
			}
			catch
			{
				return false;
			}
			finally
			{
				Marshal.FreeHGlobal(tokenInformation);
			}
		}

		private static string GetProcessUserEx(Process process)
		{
			IntPtr _SID = IntPtr.Zero;
			string SID = String.Empty;
			StringBuilder SSid = new StringBuilder(256, 512);
			StringBuilder Domain = new StringBuilder(256, 512);
			int buf_size_SSid = 256;
			int buf_size_Domain = 256;
			int t = 0;

			try
			{
				if (DumpUserInfo(process.Handle, out _SID))
				{
					LookupAccountSid(null, _SID, SSid, ref buf_size_SSid, Domain, ref buf_size_Domain, ref t);			
				}
				if (SSid.ToString() == String.Empty )
					return "Ooops";

				return SSid.ToString();
			}
			catch (Exception ex)
			{
				return "<Unknown>";
			}
		}

		[Obsolete("It works too slow")]
		private string GetProcessUserById(int pid)
		{
			try
			{
				ObjectQuery query = new ObjectQuery("Select * from Win32_Process Where ProcessID = '" + pid + "'");
				ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
				if (searcher.Get().Count == 0)
					return "<Unknown>";
				
				string ret = "<Unknown>";
				foreach (ManagementObject obj in searcher.Get())
				{
					string[] str = new String[2];
					obj.InvokeMethod("GetOwner", (object[])str);
					ret = str[0];
					if (ret == null)
						ret = "<Unknown>";
				}
				return ret;
			}
			catch
			{
				return "<Unknown>";
			}
		}

		public ProcessInfo(Process process)
		{
			try
			{
				name = process.ProcessName + ".exe";				
				priority = process.PriorityClass;
				memory = process.WorkingSet64;
				pid = process.Id;				
				cpuUsage = 0;
			}
			catch
			{
				allowed = false;
				return;
			}
			user = GetProcessUserEx(process);
		}
	}
}
